from binascii import hexlify as hx, unhexlify as uhx
from Fs.File import File
from Fs.Hfs0 import Hfs0
from Fs.Nca import Nca
import os
from Fs.IndexedFile import IndexedFile
from nut import Print
from Fs.Pfs0 import Pfs0Stream


MEDIA_SIZE = 0x200

class GamecardInfo(File, IndexedFile):
	def __init__(self, file=None):
		super(GamecardInfo, self).__init__()
		if file:
			self.open(file)

	def open(self, file, mode='rb', cryptoType=-1, cryptoKey=-1, cryptoCounter=-1):
		super(GamecardInfo, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
		self.rewind()
		self.firmwareVersion = self.readInt64()
		self.accessControlFlags = self.readInt32()
		self.readWaitTime = self.readInt32()
		self.readWaitTime2 = self.readInt32()
		self.writeWaitTime = self.readInt32()
		self.writeWaitTime2 = self.readInt32()
		self.firmwareMode = self.readInt32()
		self.cupVersion = self.readInt32()
		self.empty1 = self.readInt32()
		self.updatePartitionHash = self.readInt64()
		self.cupId = self.readInt64()
		self.empty2 = self.read(0x38)

class GamecardCertificate(File):
	def __init__(self, file=None):
		super(GamecardCertificate, self).__init__()
		self.signature = None
		self.magic = None
		self.unknown1 = None
		self.unknown2 = None
		self.data = None

		if file:
			self.open(file)

	def open(self, file, mode='rb', cryptoType=-1, cryptoKey=-1, cryptoCounter=-1):
		super(GamecardCertificate, self).open(file, mode, cryptoType, cryptoKey, cryptoCounter)
		self.rewind()
		self.signature = self.read(0x100)
		self.magic = self.read(0x4)
		self.unknown1 = self.read(0x10)
		self.unknown2 = self.read(0xA)
		self.data = self.read(0xD6)

class Xci(File, IndexedFile):
	def __init__(self, file=None, mode='rb', cryptoType=-1, cryptoKey=-1, cryptoCounter=-1):
		self.header = None
		self.signature = None
		self.magic = None
		self.secureOffset = None
		self.backupOffset = None
		self.titleKekIndex = None
		self.gamecardSize = None
		self.gamecardHeaderVersion = None
		self.gamecardFlags = None
		self.packageId = None
		self.validDataEndOffset = None
		self.gamecardInfo = None

		self.hfs0Offset = None
		self.hfs0HeaderSize = None
		self.hfs0HeaderHash = None
		self.hfs0InitialDataHash = None
		self.secureMode = None

		self.titleKeyFlag = None
		self.keyFlag = None
		self.normalAreaEndOffset = None

		self.gamecardInfo = None
		self.gamecardCert = None
		self.hfs0 = None

		File.__init__(self, file, mode, cryptoType, cryptoKey, cryptoCounter)
		IndexedFile.__init__(self, file, mode, cryptoType, cryptoKey, cryptoCounter)

	def readHeader(self):

		self.signature = self.read(0x100)
		self.magic = self.read(0x4)
		self.secureOffset = self.readInt32()
		self.backupOffset = self.readInt32()
		self.titleKekIndex = self.readInt8()
		self.gamecardSize = self.readInt8()
		self.gamecardHeaderVersion = self.readInt8()
		self.gamecardFlags = self.readInt8()
		self.packageId = self.readInt64()
		self.validDataEndOffset = self.readInt64()
		self.gamecardInfo = self.read(0x10)

		self.hfs0Offset = self.readInt64()
		self.hfs0HeaderSize = self.readInt64()
		self.hfs0HeaderHash = self.read(0x20)
		self.hfs0InitialDataHash = self.read(0x20)
		self.secureMode = self.readInt32()

		self.titleKeyFlag = self.readInt32()
		self.keyFlag = self.readInt32()
		self.normalAreaEndOffset = self.readInt32()

		self.gamecardInfo = GamecardInfo(self.partition(self.tell(), 0x70))
		self.gamecardCert = GamecardCertificate(self.partition(0x7000, 0x200))

	def open(self, path=None, mode='rb', cryptoType=-1, cryptoKey=-1, cryptoCounter=-1):
		r = super(Xci, self).open(path, mode, cryptoType, cryptoKey, cryptoCounter)
		self.readHeader()
		self.seek(0xF000)
		self.hfs0 = Hfs0(None, cryptoKey=None)
		self.partition(0xf000, None, self.hfs0, cryptoKey=None)

	def unpack(self, path):
		os.makedirs(path, exist_ok=True)

		for nspF in self.hfs0['secure']:
			filePath = os.path.abspath(path + '/' + nspF._path)
			f = open(filePath, 'wb')
			nspF.rewind()
			i = 0

			pageSize = 0x10000

			while True:
				buf = nspF.read(pageSize)
				if len(buf) == 0:
					break
				i += len(buf)
				f.write(buf)
			f.close()
			Print.info(filePath)

	def repack(self):
		newNsp = Pfs0Stream(self._path[:-4] + '.nsp')

		for nspF in self.hfs0['secure']:
			f = newNsp.add(nspF._path, nspF.size)
			nspF.rewind()
			i = 0

			pageSize = 0x10000

			while True:
				buf = nspF.read(pageSize)
				if len(buf) == 0:
					break
				i += len(buf)
				f.write(buf)

		newNsp.close()

	def verifyNcaHeaders(self):
		for f in self.hfs0['secure']:
			if not isinstance(f, Nca):
				continue
			if not f.verifyHeader():
				return False
		return True

	def printInfo(self, maxDepth=3, indent=0):
		tabs = '\t' * indent
		Print.info('\n%sXCI Archive\n' % (tabs))
		super(Xci, self).printInfo(maxDepth, indent)

		Print.info(tabs + 'magic = ' + str(self.magic))
		Print.info(tabs + 'titleKekIndex = ' + str(self.titleKekIndex))

		Print.info(tabs + 'gamecardCert = ' + str(hx(self.gamecardCert.magic + self.gamecardCert.unknown1 + self.gamecardCert.unknown2 + self.gamecardCert.data)))

		self.hfs0.printInfo(maxDepth, indent)
